package cn.geminis.crypto.tsp;

import cn.geminis.core.exception.CommonException;
import cn.geminis.core.util.DateUtils;
import cn.geminis.crypto.core.util.ProviderUtils;
import cn.geminis.crypto.core.util.X509CertificateUtils;
import cn.geminis.crypto.core.x509.X509Certificate;
import cn.geminis.crypto.csp.AbstractCspFactory;
import cn.geminis.crypto.csp.AbstractDigest;
import org.bouncycastle.asn1.ASN1Encoding;
import org.bouncycastle.asn1.ASN1Primitive;
import org.bouncycastle.asn1.cms.ContentInfo;
import org.bouncycastle.cms.SignerInformationVerifier;

import java.io.IOException;
import java.math.BigInteger;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;

/**
 * @author Allen
 */
public class TimeStampToken {

    private final org.bouncycastle.tsp.TimeStampToken bcToken;

    public TimeStampToken(byte[] data) {
        try {
            bcToken = new org.bouncycastle.tsp.TimeStampToken(ContentInfo.getInstance(data));
        } catch (Exception e) {
            throw new CommonException("解析时间戳内容错误", e);
        }
    }

    TimeStampToken(org.bouncycastle.tsp.TimeStampToken token) {
        this.bcToken = token;
    }

    public BigInteger getSerialNumber() {
        return bcToken.getTimeStampInfo().getSerialNumber();
    }

    public String getAlgOid() {
        return bcToken.getTimeStampInfo().getMessageImprintAlgOID().getId();
    }

    public LocalDateTime getTime() {
        return DateUtils.toLocalDateTime(bcToken.getTimeStampInfo().getGenTime());
    }

    public byte[] getMessageImprintData() {
        return bcToken.getTimeStampInfo().getMessageImprintDigest();
    }

    public byte[] getEncode() {
        try {
            byte[] berData = bcToken.getEncoded();
            return ASN1Primitive.fromByteArray(berData).getEncoded(ASN1Encoding.DER);
        } catch (IOException e) {
            throw new CommonException("时间戳内容编码错误", e);
        }
    }

    public void verify(List<X509Certificate> signers) {
        try {
            SignerInformationVerifier signerInformationVerifier = ProviderUtils.getSignerInformationVerifier(
                    X509CertificateUtils.findCert(signers, bcToken.getSID())
            );
            bcToken.validate(signerInformationVerifier);
        } catch (Exception e) {
            throw new CommonException("验证时间戳内容错误", e);
        }
    }

    public void verify(List<X509Certificate> signers, byte[] data) {
        verify(signers);
        // 先当作摘要值进行验证，如果不一致，则计算摘要再验证
        if (Arrays.compare(data, this.bcToken.getTimeStampInfo().getMessageImprintDigest()) != 0) {
            try (AbstractDigest digest = AbstractCspFactory.getDigest(
                    this.bcToken.getTimeStampInfo().getMessageImprintAlgOID().getId())) {
                byte[] digestData = digest.digest(data);
                if (Arrays.compare(digestData,
                        this.bcToken.getTimeStampInfo().getMessageImprintDigest()) != 0) {
                    throw new CommonException("时间戳中的数据摘要与原文不一致");
                }
            } catch (IOException e) {
                throw new CommonException("关闭摘要器异常", e);
            }
        }

    }

}
